import pygame
import time
import os
import random
import json
import numpy as np
from src.characters import Hero
from src.text_box import TextBox
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.sequence import pad_sequences

# 常量定义
PATH_POINTS_FILE = "path_points.json"
MODEL_DIR = "model"
MODEL_FILES = {
    "model": "lstm_model.h5",
    "key_to_index": "lstm_key_to_index.json", 
    "label_to_index": "lstm_label_to_index.json"
}

class Game:
    def __init__(self, screen, username=None, is_existing_user=False):
        pygame.init()
        self.screen = screen
        self.username = username
        self.clock = pygame.time.Clock()
        self.font = pygame.font.Font(None, 36)
        # 根据用户名判断用户类型
        self.is_existing_user = is_existing_user

        # 游戏状态控制
        self.state = "game"
        self.show_confirm_dialog = False
        self.selected_option = 0
        self.running = True
        self.game_over_flag = False

        text_file = self.get_random_english_text_file()
        self.text_box = TextBox(
            screen=self.screen,
            font=self.font,
            text_file=text_file,
            username=username,
            is_existing_user=self.is_existing_user
        )
        self.text_box.game = self

        # 模型系统初始化
        self.model = None
        self.key_to_index = {}
        self.label_to_index = {}
        if self.is_existing_user:
            self.initialize_model()

        # 地图系统初始化
        self.current_map_index = 0
        self.map_images = self.load_map_images()
        
        # 必须先加载当前地图，获取原始尺寸
        self.load_current_map()  # 这会设置 self.original_width 和 self.original_height
        self.original_map_size = (self.original_width, self.original_height)  # 明确设置原始地图尺寸
        
        # 然后更新屏幕区域和缩放地图
        self.update_screen_regions()
        self.scale_map()

        # 英雄初始化（必须在缩放地图之后）
        self.hero = Hero(0, 0)
        self.initialize_hero_path()

    def __enter__(self):
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cleanup()
        return False  # 不吞异常
        
    def cleanup(self):
        """彻底清理游戏状态"""
        print(f"[CLEANUP] 清理游戏实例 {id(self)}")
        self.username = None
        self.is_existing_user = False
        if hasattr(self, 'hero'):
            del self.hero
        if hasattr(self, 'text_box'):
            del self.text_box

    # === 用户身份相关方法 ===
    def is_existing_user(self):
        """检查是否为已有注册用户"""
        return self.username is None

    def initialize_model(self):
        """加载LSTM模型和相关配置文件"""
        try:
            # 加载模型
            model_path = os.path.join(MODEL_DIR, MODEL_FILES["model"])
            self.model = load_model(model_path)
            
            # 加载键位映射
            with open(os.path.join(MODEL_DIR, MODEL_FILES["key_to_index"]), 'r') as f:
                self.key_to_index = json.load(f)
                
            # 加载标签映射
            with open(os.path.join(MODEL_DIR, MODEL_FILES["label_to_index"]), 'r') as f:
                self.label_to_index = json.load(f)
                
            self.index_to_label = {v: k for k, v in self.label_to_index.items()}
            print(f"模型加载成功，用户: {self.username}")
            
        except Exception as e:
            print(f"模型加载失败: {str(e)}")
            self.model = None

    # === 资源加载相关方法 ===
    def get_random_english_text_file(self):
        """获取英文语料文件"""
        if self.is_existing_user:
            # 已有用户固定使用指定文件
            fixed_path = "assets/texts/existing_user_texts/english.txt"
            if os.path.exists(fixed_path):
                return fixed_path
            # 如果文件不存在，fallback到默认路径
            return "assets/texts/english.txt"
        else:
            # 新用户随机选择文本
            texts_dir = "assets/texts"
            english_files = [
                f for f in os.listdir(texts_dir) 
                if f.startswith("english") and f.endswith(".txt")
            ]
            return os.path.join(
                texts_dir, 
                random.choice(english_files) if english_files else "english.txt"
            )
    
    def load_map_images(self):
        """加载所有地图图片"""
        map_images = []
        map_dir = "assets/maps"
        for file in os.listdir(map_dir):
            if file.endswith(".png"):
                map_images.append(os.path.join(map_dir, file))
        return map_images
    
    def load_current_map(self):
        """加载当前地图资源"""
        self.map_image = pygame.image.load(
            self.map_images[self.current_map_index])
        # 确保这两个属性被正确设置
        self.original_width, self.original_height = self.map_image.get_size()
        self.map_rect = self.map_image.get_rect()
        
        # 地图切换相关
        self.next_map_image = None
        self.map_transition = False
        self.transition_start_time = 0
        self.transition_duration = 3  # 切换动画持续时间(秒)

    def scale_map(self):
        """缩放地图并确保scaled_map_image存在"""
        # 确保地图已加载
        if not hasattr(self, 'map_image'):
            self.load_current_map()

        # 计算缩放比例
        map_aspect = self.original_width / self.original_height
        region_aspect = self.map_region.width / self.map_region.height

        if map_aspect > region_aspect:
            new_width = self.map_region.width
            new_height = int(new_width / map_aspect)
        else:
            new_height = self.map_region.height
            new_width = int(new_height * map_aspect)

        # 确保始终创建scaled_map_image
        self.scaled_map_image = pygame.transform.scale(
            self.map_image, (new_width, new_height))
        
        # 计算居中位置
        self.map_x = (self.map_region.width - new_width) // 2
        self.map_y = (self.map_region.height - new_height) // 2

    def initialize_hero_path(self):
        """初始化英雄移动路径"""
        self.hero.load_path(
            path_file=PATH_POINTS_FILE,
            map_width=self.original_width,
            map_height=self.original_height,
            scaled_width=self.scaled_map_image.get_width(),
            scaled_height=self.scaled_map_image.get_height(),
            map_x=self.map_x,
            map_y=self.map_y
        )
        
        # 初始位置信息
        print(f"英雄初始位置: {self.hero.logical_pos}")
        if len(self.hero.path_points) > 1:
            print(f"下一个路径点: {self.hero.path_points[1]}")

    # === 显示系统相关方法 ===
    def update_screen_regions(self):
        """更新屏幕区域划分（包含地图初始缩放）"""
        screen_width, screen_height = self.screen.get_size()

        # 1. 先设置地图区域（占屏幕90%高度）
        self.map_region = pygame.Rect(0, 0, screen_width, int(screen_height * 0.9))

        # 2. 如果地图尚未加载，先加载默认地图
        if not hasattr(self, 'map_image'):
            self.load_current_map()

        # 3. 执行地图缩放（这会创建scaled_map_image）
        self.scale_map()

        # 4. 更新文本框区域（占屏幕10%高度）
        text_box_height = screen_height - self.map_region.height
        self.text_box.box_rect = pygame.Rect(
            0, self.map_region.height,
            screen_width, text_box_height
        )
        # 根据用户类型更新文本框区域
        self.text_box.is_existing_user = self.is_existing_user
        self.text_box.update_text_area()

        

    # === 游戏主循环 ===
    def run(self):
        """运行游戏主循环"""
        self.text_box.start_timer()
        
        while self.running:
            self.handle_events()
            
            if not self.game_over_flag:
                self.update()
                self.render()
            else:
                # 游戏结束后显示结束消息并处理返回值
                should_return = self.show_end_message()
                if should_return:
                    return  # 返回主菜单
                else:
                    self.running = False  # 完全退出
            
            self.clock.tick(60)
        
        pygame.quit()

    def handle_events(self):
        """处理所有输入事件"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            
            # 确认对话框事件处理
            elif self.show_confirm_dialog:
                if self.handle_confirm_dialog_event(event):
                    self.game_over()
                    return
                
            # 常规游戏事件处理
            else:
                self.handle_normal_event(event)

    def handle_normal_event(self, event):
        """处理常规游戏事件"""
        if event.type == pygame.VIDEORESIZE:
            self.handle_resize(event)
            
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                self.text_box.pause_timer()
                self.show_confirm_dialog = True
                self.selected_option = 0
        
        # 传递事件给文本框处理
        self.text_box.handle_event(event)

    def handle_confirm_dialog_event(self, event):
        """处理确认对话框事件"""
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                self.selected_option = 0
            elif event.key == pygame.K_RIGHT:
                self.selected_option = 1
            elif event.key == pygame.K_RETURN:
                if self.selected_option == 1:  # 确认退出
                    return True
                else:  # 取消退出
                    self.show_confirm_dialog = False
                    self.text_box.resume_timer()
            elif event.key == pygame.K_ESCAPE:
                self.show_confirm_dialog = False
                self.text_box.resume_timer()
        
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_pos = pygame.mouse.get_pos()
            if hasattr(self, 'confirm_button_rect') and \
               self.confirm_button_rect.collidepoint(mouse_pos):
                return True
            elif hasattr(self, 'cancel_button_rect') and \
                 self.cancel_button_rect.collidepoint(mouse_pos):
                self.show_confirm_dialog = False
                self.text_box.resume_timer()
        
        return False

    def update(self):
        """更新游戏状态"""
        if hasattr(self, 'hero'):
            self.hero.update()
            
            # 检查是否到达终点
            if self.hero.has_reached_end() and not self.game_over_flag:
                self.game_over()
                
            # 检查地图切换
            self.check_map_transition()
            
            # 更新显示位置
            map_rect = pygame.Rect(self.map_x, self.map_y, 
                                self.scaled_map_image.get_width(), 
                                self.scaled_map_image.get_height())
            self.hero.calculate_display_position(map_rect, self.original_map_size)

    def check_map_transition(self):
        """检查是否需要切换地图"""
        if hasattr(self, 'hero') and hasattr(self.hero, 'current_path_index'):
            current_point = tuple(self.hero.path_points[self.hero.current_path_index])
            
            if current_point in self.hero.map_change_points and not self.map_transition:
                next_map_index = self.hero.map_change_points[current_point] % len(self.map_images)
                
                if next_map_index != self.current_map_index:
                    self.start_map_transition(next_map_index)

    def start_map_transition(self, next_map_index):
        """开始地图切换动画"""
        self.map_transition = True
        self.transition_start_time = time.time()
        
        # 加载新地图并保持与当前相同的显示尺寸
        self.next_map_image = pygame.image.load(self.map_images[next_map_index])
        self.next_map_image = pygame.transform.scale(
            self.next_map_image,
            (self.scaled_map_image.get_width(), self.scaled_map_image.get_height())
        )

    def complete_map_transition(self):
        """完成地图切换（仅视觉效果）"""
        self.current_map_index = (self.current_map_index + 1) % len(self.map_images)
        self.load_current_map()
        
        # 保持相同的缩放比例
        self.scale_map()
        
        self.map_transition = False
        self.next_map_image = None

    def render(self):
        """渲染游戏"""
        self.screen.fill((255, 255, 255))
        
        # 渲染地图
        self.render_map()
        
        # 英雄显示位置已在update中更新，直接绘制
        self.hero.draw(self.screen)
        
        # 渲染文本框
        self.text_box.render()
        
        # 渲染确认对话框
        if self.show_confirm_dialog:
            self.render_confirm_dialog()
        
        pygame.display.flip()

    def render_map(self):
        """渲染地图（含切换动画效果）"""
        if self.map_transition:
            progress = min((time.time() - self.transition_start_time) / self.transition_duration, 1)
            
            # 淡出当前地图
            self.scaled_map_image.set_alpha(int(255 * (1 - progress)))
            self.screen.blit(self.scaled_map_image, (self.map_x, self.map_y))
            
            # 淡入新地图
            self.next_map_image.set_alpha(int(255 * progress))
            self.screen.blit(self.next_map_image, (self.map_x, self.map_y))
            
            if progress >= 1:
                self.complete_map_transition()
        else:
            # 正常绘制单张地图
            self.scaled_map_image.set_alpha(255)
            self.screen.blit(self.scaled_map_image, (self.map_x, self.map_y))

    def render_confirm_dialog(self):
        """渲染确认退出对话框"""
        # 半透明背景
        overlay = pygame.Surface(self.screen.get_size(), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 128))
        self.screen.blit(overlay, (0, 0))
        
        # 对话框框体
        dialog_width = min(500, self.screen.get_width() - 40)
        dialog_height = min(200, self.screen.get_height() - 40)
        dialog_rect = pygame.Rect(
            (self.screen.get_width() - dialog_width) // 2,
            (self.screen.get_height() - dialog_height) // 2,
            dialog_width,
            dialog_height
        )
        pygame.draw.rect(self.screen, (240, 240, 240), dialog_rect)
        pygame.draw.rect(self.screen, (0, 0, 0), dialog_rect, 2)
        
        # 文字提示
        try:
            font = pygame.font.Font("assets/fonts/KeLaiYinLanDeYeWan-2.ttf", 24)
        except:
            font = pygame.font.Font(None, 24)
            
        text = font.render("确定要返回主菜单吗？", True, (0, 0, 0))
        text_rect = text.get_rect(center=(
            dialog_rect.centerx,
            dialog_rect.y + 40
        ))
        self.screen.blit(text, text_rect)
        
        # 按钮
        button_width = 100
        button_height = 40
        button_y = dialog_rect.y + dialog_height - 70
        
        # 取消按钮
        self.cancel_button_rect = pygame.Rect(
            dialog_rect.centerx - button_width - 20,
            button_y,
            button_width,
            button_height
        )
        pygame.draw.rect(self.screen, (0, 200, 0), self.cancel_button_rect)
        cancel_text = font.render("取消", True, (0, 0, 0))
        self.screen.blit(cancel_text, cancel_text.get_rect(
            center=self.cancel_button_rect.center))
        
        # 确认按钮
        self.confirm_button_rect = pygame.Rect(
            dialog_rect.centerx + 20,
            button_y,
            button_width,
            button_height
        )
        pygame.draw.rect(self.screen, (200, 0, 0), self.confirm_button_rect)
        confirm_text = font.render("确认", True, (0, 0, 0))
        self.screen.blit(confirm_text, confirm_text.get_rect(
            center=self.confirm_button_rect.center))
        
        # 高亮选中的按钮
        if self.selected_option == 0:
            pygame.draw.rect(self.screen, (255, 255, 0), self.cancel_button_rect, 3)
        else:
            pygame.draw.rect(self.screen, (255, 255, 0), self.confirm_button_rect, 3)

    def handle_resize(self, event):
        """处理窗口大小变化"""
        self.screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
        self.update_screen_regions()
        
        # 通知英雄更新显示位置
        if hasattr(self, 'hero'):
            map_rect = pygame.Rect(self.map_x, self.map_y, 
                                self.scaled_map_image.get_width(), 
                                self.scaled_map_image.get_height())
            self.hero.calculate_display_position(map_rect, self.original_map_size)

    def game_over(self):
        """游戏结束处理"""
        self.game_over_flag = True
        
        # 保存用户数据
        if self.is_existing_user:
            self.text_box.export_existing_user_data("current_user_data.json")
        elif self.text_box.valid_key_count > 400:
            os.makedirs("assets/user_data", exist_ok=True)
            # 确保使用self.username作为文件名
            user_data_path = f"assets/user_data/{self.username}.json"
            self.text_box.export_key_events_to_json(user_data_path)
        
        # 显示结束消息并获取返回状态
        should_return = self.show_end_message()
        
        if should_return:
            self.running = False

    def show_end_message(self):
        """显示游戏结束消息并处理返回"""
        # 创建新的Surface如果已被销毁
        if not pygame.display.get_init():
            pygame.display.init()
            self.screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE)
        
        self.screen.fill((255, 255, 255))
        
        try:
            font = pygame.font.Font("assets/fonts/KeLaiYinLanDeYeWan-2.ttf", 48)
        except:
            font = pygame.font.Font(None, 48)
        
        text = font.render("恭喜完成旅程，按任意键返回！", True, (0, 0, 0))
        text_rect = text.get_rect(center=self.screen.get_rect().center)
        self.screen.blit(text, text_rect)
        pygame.display.flip()
        
        # 等待用户按键
        waiting = True
        while waiting:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    return False
                elif event.type in (pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN):
                    waiting = False
        
        # 仅标记需要返回，不在这里退出pygame
        return True